[Chapter 10] 10.8 Testing sendmail.cf
Author: Craig Hunt
Date: 1999-02-04T01:39:58Z

#

| Chapter 10 sendmail |

## 10.8 Testing sendmail.cf

sendmail provides powerful tools for configuration testing and debugging. These test tools are invoked on the sendmail command line using some of the many sendmail command-line arguments. Appendix E lists all of the command-line arguments; Table 10.6 summarizes those that relate to testing and debugging.

Table 10.6: sendmail Arguments for Testing and Debugging
Argument | Function
--- | ---
-t | Send to everyone listed in To:, Cc:, and Bcc:.
-bt | Run in test mode.
-bv | Verify addresses; don't collect or deliver mail.
-bp | Print the mail queue.
-C`file` | Use `file` as the configuration file.
-d`level` | Set debugging level.
-O`option`=`value` | Set `option` to the specified `value`.
-e | Defines how errors are returned.
-v | Run in verbose mode.

Some command-line arguments are used to verify address processing and to gain confidence in the new configuration. Once you think your configuration will work, choose friends at various sites and send them mail. Use the -C argument to read the test configuration file and the -v argument to display the details of the mail delivery. -v displays the complete SMTP exchange between the two hosts.

By observing if your mailer properly connects to the remote mailer and formats the addresses correctly, you'll get a good idea of how the configuration is working. The following example is a test from peanut using the test.cf configuration file we just created:

>     peanut# /usr/lib/sendmail -Ctest.cf -t -v
>     To: craigh@ora.com
>     From: craig
>     Subject: Sendmail Test
>     Ignore this test.
>     ^D
>     craigh@ora.com... Connecting to ora.com. via smtp...
>     220-ruby.ora.com Sendmail 8.6.13/8.6.11 ready at Sat, 16 Nov 1996
>     220 ESMTP spoken here
>     >>> EHLO peanut.nuts.com
>     250-ruby.ora.com Hello craig@peanut.nuts.com [172.16.12.2], pleased
>          to meet you
>     250-EXPN
>     250-SIZE
>     250 HELP
>     >>> MAIL From:<craig@peanut.nuts.com> SIZE=64
>     250 <craig@peanut.nuts.com>... Sender ok
>     >>> RCPT To:<craigh@ora.com>
>     250 <craigh@ora.com>... Recipient ok
>     >>> DATA
>     354 Enter mail, end with "." on a line by itself
>     >>> .
>     250 SAA27399 Message accepted for delivery
>     craigh@ora.com... Sent (SAA27399 Message accepted for delivery)
>     Closing connection to ora.com.
>     >>> QUIT
>     221 ruby.ora.com closing connection

We entered everything before the CTRL-D (^D). Everything after the ^D was displayed by sendmail. Figure 10.5 highlights some of the important information in this display, and notes the sendmail macros that relate to the highlighted material.

#### Figure 10.5: Verbose mail output

This test successfully transfers mail to a remote Internet site. The sendmail output shows that peanut sent the mail to ora.com via the smtp mail delivery program. The sendmail greeting shows that the remote host handling this SMTP connection is ruby.ora.com. Therefore, ruby must be the mail server for the ora.com domain; i.e., the MX record for ora.com points to ruby.ora.com.

The ESMTP and EHLO messages indicate that both peanut and ruby use Extended Simple Mail Transfer Protocol (ESMTP).

Everything worked just fine! We could quit right now and use this configuration. But like most computer people, we cannot stop ourselves from tinkering in order to make things "better."

The From: address, craig@peanut.nuts.com, is clearly a valid address but it is not quite what we want. What we want is to have people address us as firstname.lastname@domain - not as user@host.domain, which is exactly the configuration we created earlier in this chapter with a few lines of m4 code. We will create the same configuration here to provide an example of how to use the various troubleshooting tools that come with sendmail. However, if you really want to make major sendmail configuration changes, you should use m4 to build your configuration.

Most changes to sendmail.cf are small and are made near the beginning of the file in the Local Information section. Looking closely at that section provides the clues we need to solve part of our configuration problem.

Without knowing what "masquerading" means, the comments for class E, class M, and macro M lead us to guess that the value set for macro M will be used to rewrite the hostname. [22] In particular, the comment "names that should be exposed as from this host, even if we masquerade" led me to believe that masquerading hides the hostname. Based on this guess, we set a value for macro M as follows:

> [22] In the m4 source file we configured masquerading with the MASQUERADE_AS(nuts.com) command.

>     # who I masquerade as (null for no masquerading) (see also $=M)
>     DMnuts.com

Are we sure that setting a value for the M macro will hide the hostname? No, but changing the value in test.cf and running another test will do no harm. Running the test program with the test configuration has no affect on the running sendmail daemon that was started by the sendmail -bd -q1h command in the boot script. Only an instantiation of sendmail with the -Ctest.cf argument will use the test.cf test configuration.

### 10.8.1 Testing Rewrite Rules

In the initial test, the From: address went into sendmail as craig, and it came out as craig@peanut.nuts.com. Obviously it has been rewritten. This time we test whether the change we made to the macro M in the configuration files modifies the rewrite process by directly testing the rewrite rulesets. First, we need to find out what rules were used to rewrite this address. To get more information, we run sendmail with the -bt option.

When sendmail is invoked with the -bt option, it prompts for input using the greater than symbol (>). At the prompt, enter one of the test commands shown in Table 10.7

Table 10.7: sendmail Testing Commands
Command | Function
--- | ---
`ruleset`[,`ruleset`]... `address` | Process `address` through `ruleset`(s).
.D`mvalue` | Assign `value` to macro `m`.
.C`cvalue` | Add `value` to class `c`.
=S`ruleset` | Display the rules in `ruleset`.
=M | Display the mailer definitions.
-d`value` | Set the debug flag to `value`.
$`m` | Display the value of macro `m`.
$=`c` | Display the contents of class `c`.
/mx `host` | Display the MX records for `host`.
/parse `address` | Return the mailer/host/user triple for `address`.
/try `mailer address` | Process `address` for `mailer`.
/tryflags `flags` | Set the address processed by /parse or /try to H (Header), E (Envelope), S (Sender), or R (Recipient).
/canon `hostname` | Canonify `hostname`.
/map `mapname key` | Display the value for `key` found in `mapname`.

The most basic test is a ruleset number followed by an email address. The address is the test data, and the ruleset number is the ruleset to be tested. The address is easy to select; it is the one that was improperly rewritten. But how do you know which ruleset to specify?

Use Figure 10.4 to determine which rulesets to enter. Ruleset 3 is applied to all addresses. It is followed by different rulesets depending on whether the address is a delivery address, a sender address, or a recipient address. Furthermore, the rulesets used for sender and recipient addresses vary depending on the mailer that is used to deliver the mail. All addresses are then processed by ruleset 4.

There are two variables in determining the rulesets used to process an address: the type of address and the mailer through which it is processed. The three address types are delivery address, recipient address, and sender address. You know the address type because you select the address being tested. In our test mail we were concerned about the sender address. Which mailer is used is determined by the delivery address. To find out which mailer delivered the test mail, run sendmail with the -bv argument and the delivery address:

>     # sendmail -bv craigh@ora.com
>     craigh@ora.com... deliverable: mailer smtp, host ora.com.,
>          user craigh@ora.com

Knowing the mailer, we can use sendmail with the -bt option to process the sender `From:` address. There are two types of sender addresses: the sender address in the "envelope" and the sender address in the message header. The message header address is the one on the `From:` line sent with the message during the SMTP DATA transfer. You probably see it in the mail headers when you view the message with your mail reader. The "envelope" address is the address used during the SMTP protocol interactions. sendmail allows us to view the processing of both of these addresses:

>     # /usr/lib/sendmail -bt -Ctest.cf
>     ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
>     Enter <ruleset> <address>
>     > /tryflags HS
>     > /try smtp craig
>     Trying header sender address craig for mailer smtp
>     rewrite: ruleset  3   input: craig
>     rewrite: ruleset 96   input: craig
>     rewrite: ruleset 96 returns: craig
>     rewrite: ruleset  3 returns: craig
>     rewrite: ruleset  1   input: craig
>     rewrite: ruleset  1 returns: craig
>     rewrite: ruleset 31   input: craig
>     rewrite: ruleset 51   input: craig
>     rewrite: ruleset 51 returns: craig
>     rewrite: ruleset 61   input: craig
>     rewrite: ruleset 61 returns: craig < @ *LOCAL* >
>     rewrite: ruleset 93   input: craig < @ *LOCAL* >
>     rewrite: ruleset 93 returns: craig < @ nuts . com . >
>     rewrite: ruleset 31 returns: craig < @ nuts . com . >
>     rewrite: ruleset  4   input: craig < @ nuts . com . >
>     rewrite: ruleset  4 returns: craig @ nuts . com
>     Rcode = 0, addr = craig@nuts.com
>     > /tryflags ES
>     > /try smtp craig
>     Trying envelope sender address craig for mailer smtp
>     rewrite: ruleset  3   input: craig
>     rewrite: ruleset 96   input: craig
>     rewrite: ruleset 96 returns: craig
>     rewrite: ruleset  3 returns: craig
>     rewrite: ruleset  1   input: craig
>     rewrite: ruleset  1 returns: craig
>     rewrite: ruleset 11   input: craig
>     rewrite: ruleset 51   input: craig
>     rewrite: ruleset 51 returns: craig
>     rewrite: ruleset 61   input: craig
>     rewrite: ruleset 61 returns: craig < @ *LOCAL* >
>     rewrite: ruleset 94   input: craig < @ *LOCAL* >
>     rewrite: ruleset 94 returns: craig < @ peanut . nuts . com . >
>     rewrite: ruleset 11 returns: craig < @ peanut . nuts . com . >
>     rewrite: ruleset  4   input: craig < @ peanut . nuts . com . >
>     rewrite: ruleset  4 returns: craig @ peanut . nuts . com
>     Rcode = 0, addr = craig@peanut.nuts.com
>     > ^D

The /tryflags command defines the type of address to be processed by a /try or a /parse command. Four flags are available for the /tryflags command: S for sender, R for recipient, H for header, and E for envelope. By combining two of these flags, the first /tryflags command says we will process a header sender (HS) address. The /try command tells sendmail to process the address through a specific mailer. In the example, we process the email address craig through the mailer smtp. First, we process it as the header sender address, and then as the envelope sender address. From this test, we can tell that the value that we entered in the M macro is used to rewrite the sender address in the message header but it is not used to rewrite the sender address in the envelope.

Unfortunately, older versions of sendmail, such as the version that comes with Solaris 2.5.1, don't support /try and /tryflags. Testing these older systems requires a little more effort. Knowing the mailer is still the key to determining the rulesets called to process the sender `From:` address. A grep of the test.cf file displays the rulesets that the smtp mailer uses for sender addresses.

>     % grep ^Msmtp /etc/sendmail.cf
>     Msmtp,     P=[IPC], F=mDFMuX, S=11/31, R=21, E=\r\n, L=990,
>     Msmtp8,    P=[IPC], F=mDFMuX8, S=11/31, R=21, E=\r\n, L=990,

Again, refer to Figure 10.4 It shows that the sender address goes through ruleset 3, ruleset 1, the ruleset specified by S, and ruleset 4. The mailer definition for smtp in our sample configuration defines two rulesets for S - 11 and 31. [23] The first ruleset is used for rewriting the sender address in the "envelope" and the second is used to rewrite the sender address in the message header.

> [23] Many versions of sendmail define only one ruleset each for S and R.

Based on the information in Figure 10.4 and in the S field of the smtp mailer, we know that the rulesets that process the message header sender address are 3, 1, 31 and 4. So we run sendmail with the -bt option and enter 3,1,31,4 craig at the command prompt. This command processes the sender address through each of these rulesets in succession. We also know that the envelope sender address is processed by rulesets 3, 1, 11, and 4. To test that, we enter 3,1,11,4 craig.

The results of these tests are exactly the same as those shown in the example above. The value of the M macro rewrites the hostname in the message sender address just as we wanted. The hostname in the envelope sender address is not rewritten. Usually this is acceptable. However, we want to create exactly the same configuration as in the m4 example. The FEATURE(masquerade_envelope) command used in the m4 example causes the envelope sender address to be rewritten. Therefore, we want this configuration to also rewrite it.

The only difference between how the message and envelope addresses are processed is that one goes through ruleset 31 and the other goes through ruleset 11. The tests show that both rulesets call ruleset 51 and then ruleset 61. They diverge at that point because ruleset 31 calls ruleset 93 and ruleset 11 calls ruleset 94. The tests also show that ruleset 93 provides the address rewrite that we want for the message sender address, while the envelope sender address is not processed in the manner we desire by ruleset 94. The test.cf code for rulesets 94, 11, and 31 is shown below:

>     ###################################################################
>     ###  Ruleset 94 -- convert envelope names to masquerade form    ###
>     ###################################################################
>     S94
>     #R$+                    $@ $>93 $1
>     R$* < @ *LOCAL* > $*    $: $1 < @ $j . > $2

>     #
>     #  envelope sender rewriting
>     #
>     S11
>     R$+             $: $>51 $1      sender/recipient common
>     R$* :; <@>      $@              list:; special case
>     R$*             $: $>61 $1      qualify unqual'ed names
>     R$+             $: $>94 $1      do masquerading

>     #
>     #  header sender and masquerading header recipient rewriting
>     #
>     S31
>     R$+             $: $>51 $1      sender/recipient common
>     R:; <@>         $@              list:; special case
>     R$* <@> $*      $@ $1 <@> $2    pass null host through
>     R< @ $* > $*    $@ < @ $1 > $2  pass route-addr through
>     R$*             $: $>61 $1      qualify unqual'ed names
>     R$+             $: $>93 $1      do masquerading

Clearly, ruleset 94 does not do what we want and ruleset 93 does. A quick inspection of ruleset 94 shows that it does not contain a single reference to macro M. Yet the comment on the line in ruleset 11 that calls it indicates that ruleset 94 should "do masquerading." The first line of ruleset 94 calls ruleset 93, but it is commented out. Our solution is to uncomment the first line of ruleset 94 so that it now calls ruleset 93, which is the ruleset that really does the masquerade processing.

Debugging a sendmail.cf file is more of an art than a science. Deciding to edit the first line of ruleset 94 to call ruleset 93 is little more than a hunch. The only way to verify the hunch is through testing. We run sendmail -bt -Ctest.cf again to test the addresses craig, craig@peanut, and craig@localhost through rulesets 3, 1, 11, and 4. All tests run successfully, rewriting the various input addresses into craig@nuts.com. We then retest by sending mail via sendmail -v -t -Ctest.cf. Only when all of these tests run successfully do we really believe in our hunch and move on to the next task, which is to rewrite the user part of the email address into the user's first and last names.

### 10.8.2 Using Key Files in sendmail

The last feature we added to the m4 source file was FEATURE(genericstable), which adds a database process to the configuration that we use to convert the user portion of the email address from the user's login name to the user's first and last names. To do the same thing here, create a text file of login names and first and last names and build a database with makemap. [24]

> [24] See the m4 section for more information about makemap.

>     # cat realnames
>     dan Dan.Scribner
>     tyler Tyler.McCafferty
>     pat Pat.Stover
>     willy Bill.Wright
>     craig Craig.Hunt
>     # makemap dbm realnames < realnames

Once the database is created, define it for sendmail. Use the K command to do this. To use the database that we have just built, insert the following lines into the Local Information section of the sendmail.cf file:

>     # define a database to map login names to firstname.lastname
>     Krealnames dbm /etc/realnames

The K command defines realnames as the internal sendmail name of this database. Further, it identifies that this is a database of type dbm and that the path to the database is /etc/realnames. sendmail adds the correct filename extensions to the pathname depending on the type of the database, so you don't need to worry about it.

Finally, we add a new rule that uses the database to rewrite addresses. We add it to ruleset 11 and ruleset 31 immediately after the lines in those rulesets that call ruleset 93. This way, our new rule gets the address as soon as ruleset 93 finishes processing it.

>     # when masquerading convert login name to firstname.lastname
>     R$-<@$M.>$*    $:$(realnames $1 $)<@$2.>$3    user=>first.last

This rule is designed to process the output of ruleset 93, which rewrites the hostname portion of the address. Addresses that meet the criteria to have the hostname part rewritten are also the addresses for which we want to rewrite the user part. Look at the output of ruleset 93 from the earlier test. That address, craig<@nuts.com.>, matches the pattern `$-<@$M.>$*`. The address has exactly one token (craig) before the literal <@, followed by the value of M (nuts.com), the literal .> and zero tokens.

The transformation part of this rule takes the first token ($1) from the input address and uses it as the key to the realnames database, as indicated by the `$:$(realnames $1 $)` syntax. For the sample address craig<@nuts.com>, $1 is craig. When used as an index into the database realnames shown at the beginning of this section, it returns Craig.Hunt. This returned value is prepended to the literal <@, the value of indefinite token $2, the literal .>, and the value of $3, as indicated by the `<@$2.>$3` part of the transformation. The effect of this new rule is to convert the username to the user's real first and last names.

After adding the new rule to rulesets 11 and 31, a test yields the following results:

>     # sendmail -bt -Ctest.cf
>     ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
>     Enter <ruleset> <address>
>     > 3,1,11,4 craig
>     rewrite: ruleset  3   input: craig
>     rewrite: ruleset 96   input: craig
>     rewrite: ruleset 96 returns: craig
>     rewrite: ruleset  3 returns: craig
>     rewrite: ruleset  1   input: craig
>     rewrite: ruleset  1 returns: craig
>     rewrite: ruleset 11   input: craig
>     rewrite: ruleset 51   input: craig
>     rewrite: ruleset 51 returns: craig
>     rewrite: ruleset 61   input: craig
>     rewrite: ruleset 61 returns: craig < @ *LOCAL* >
>     rewrite: ruleset 93   input: craig < @ *LOCAL* >
>     rewrite: ruleset 93 returns: craig < @ nuts . com . >
>     rewrite: ruleset 11 returns: Craig . Hunt < @ nuts . com . >
>     rewrite: ruleset  4   input: Craig . Hunt < @ nuts . com . >
>     rewrite: ruleset  4 returns: Craig . Hunt @ nuts . com
>     > 3,1,31,4 craig
>     rewrite: ruleset  3   input: craig
>     rewrite: ruleset 96   input: craig
>     rewrite: ruleset 96 returns: craig
>     rewrite: ruleset  3 returns: craig
>     rewrite: ruleset  1   input: craig
>     rewrite: ruleset  1 returns: craig
>     rewrite: ruleset 31   input: craig
>     rewrite: ruleset 51   input: craig
>     rewrite: ruleset 51 returns: craig
>     rewrite: ruleset 61   input: craig
>     rewrite: ruleset 61 returns: craig < @ *LOCAL* >
>     rewrite: ruleset 93   input: craig < @ *LOCAL* >
>     rewrite: ruleset 93 returns: craig < @ nuts . com . >
>     rewrite: ruleset 31 returns: Craig . Hunt < @ nuts . com . >
>     rewrite: ruleset  4   input: Craig . Hunt < @ nuts . com . >
>     rewrite: ruleset  4 returns: Craig . Hunt @ nuts . com
>     > ^D

If the tests do not give the results you want, make sure that you have correctly entered the new rewrite rules and that you have correctly built the database. If sendmail complains that it can't lock the database file, you need to download a more recent release of sendmail V8. The following error message could also be displayed:

>     test.cf: line 116: readcf: map realnames: class dbm not available

This indicates that your system does not support dbm databases. Change the database type on the K command line to hash and rerun sendmail -bt. If it complains again, try it with btree. When you find a type of database that your sendmail likes, rerun makemap using that database type. If your sendmail doesn't support any database type, see Appendix E for information on re-compiling sendmail with database support.

Note that all of the changes made directly to the sendmail.cf file in the second half of this chapter (masquerading the sender address, masquerading the envelope address and converting usernames) were handled by just three lines in the m4 source file. These examples were used to demonstrate how to use the sendmail test tools. If you really need to make a new, custom configuration, use m4. It is easiest to maintain and enhance the sendmail configuration through the m4 source file.

10.7 Modifying a sendmail.cf File |  | 10.9 Summary